/**
 * @FileName: commonConnectionPool.cc
 * @Author: 李波(Edwin Lee)
 * @Date: 2024-04-19 13:14:34
 * @Version: 1.0
 * @Description: 
 */
#include "commonConnectionPool.h"
#include "public.h"
#include <cstdlib>
#include <muduo/base/Logging.h>
using namespace std;
ConnectionPool& ConnectionPool::getInstance()
{
	static ConnectionPool pool;
	return pool;
}

bool ConnectionPool::loadConfigFile(string filename)
{
	std::unordered_map<std::string,std::string> config;
	if(!::loadConfigFile(config,filename))
	{
		LOG_INFO << "无法打开配置文件%s";
		return false;
	}
	auto end = config.end();
	if(end==config.find("ip")
			|| end==config.find("port")
			|| end==config.find("dbname")
			|| end==config.find("username")
			|| end==config.find("password")
			|| end==config.find("initSize")
			|| end==config.find("maxSize")
			|| end==config.find("maxIdleTime")
			|| end==config.find("connectionTimeout"))
	{
		LOG_FATAL << "配置文件缺少配置项";
	}
	ip_=config["ip"];
	port_=static_cast<unsigned short>(atoi(config["port"].c_str()));
	dbname_=config["dbname"];
	username_=config["username"];
	password_=config["password"];
	initSize_=atoi(config["initSize"].c_str());
	maxSize_=atoi(config["maxSize"].c_str());
	maxIdleTime_=atoi(config["maxIdleTime"].c_str());
	connectionTimeout_=atoi(config["connectionTimeout"].c_str());
	return true;
}

ConnectionPool::ConnectionPool()
{
	if(!loadConfigFile())
	{
		LOG_FATAL << "加载配置文件出错";
		return;
	}
	LOG_INFO <<"加载配置文件成功";

	//初始化initSize个链接
	Connection* conn;
	for(int i=0; i<initSize_; ++i)
	{
		conn = new Connection();
		if(!conn->connect(ip_,port_,username_, password_, dbname_))
		{
			break;
		}
		conn->refreshAliveTime();
		connectionQue_.push(conn);
		++connCount_;
	}
	//开启生产线程
	thread produce(std::bind(&ConnectionPool::produceConnectionTask,this));
	produce.detach();

	//启动一个新线程回收超过maxIdleTime的空闲链接
	thread recycle(std::bind(&ConnectionPool::recyleConnectionTask,this));
	recycle.detach();
	LOG_INFO << "end connectionpool";
}

void ConnectionPool::produceConnectionTask()
{
	while(true)
	{
		unique_lock<mutex> lock(queueMutex_);
		while(!connectionQue_.empty())
		{
			cond_.wait(lock);
		}
		if(connCount_<maxSize_)
		{	
			Connection* conn = new Connection();
			conn->connect(ip_,port_,username_, password_, dbname_);
			conn->refreshAliveTime();
			connectionQue_.push(conn);
			++connCount_;
		}
		cond_.notify_all();
	}
}

shared_ptr<Connection> ConnectionPool::getConnection()
{
	unique_lock<mutex> lock(queueMutex_);
	while(connectionQue_.empty())
	{
		if(cv_status::timeout==cond_.wait_for(lock,chrono::milliseconds(connectionTimeout_)))
		{
			LOG_INFO << "获取空闲链接超时..获取链接失败！";
			return nullptr;
		}
	}
	/*
	shared_ptr智能指针析构时，会把connection资源直接delete掉，相当于
	调用connection的析构函数，connection就被close掉了。
	这里需要自定义shared_ptr的释放资源的方式，把connection直接归还到queue当中
	delete函数的参数和源指针的参数类型相同，会把原指针传入参数
	*/
	shared_ptr<Connection> conn(connectionQue_.front(),[&](Connection* pcon){
			// 这里是在服务器应用线程中调用的，所以一定要考虑队列的线程安全操作
			unique_lock<mutex> lock(queueMutex_);
			pcon->refreshAliveTime();
			connectionQue_.push(pcon);
			});
	connectionQue_.pop();
	cond_.notify_all();
	return conn;
}

void ConnectionPool::recyleConnectionTask()
{
	for(;;)
	{
		//通过sleep模拟定时效果
		this_thread::sleep_for(chrono::seconds(maxIdleTime_));
		//扫描整个队列，释放多余的链接
		unique_lock<mutex> lock(queueMutex_);
		Connection* p=nullptr;
		while(connCount_>initSize_)
		{
			p=connectionQue_.front();
			if(p->getAliveTime() >= (maxIdleTime_*1000))
			{
				connectionQue_.pop();
				--connCount_;
				delete p;
			}
			else 
			{
				//队列头没有超时，其他的也不会超时
				break;
			}
		}

	}
}
